package com.reyco1.medusa.data.binding
{
	import com.reyco1.medusa.abstract.SmartEventDispatcher;
	import com.reyco1.medusa.core.IDisposable;
	import com.reyco1.medusa.debug.Debugger;
	import com.reyco1.medusa.events.DataEvent;
	
	import flash.events.Event;
	import flash.events.IEventDispatcher;
	import flash.utils.Dictionary;
	import flash.utils.Proxy;
	import flash.utils.flash_proxy;
	
	
	public dynamic class BindableCore extends Proxy implements IDisposable, IEventDispatcher
	{
		private var dispatcher:SmartEventDispatcher;
		private var item:Dictionary;
		private var propertyList:Array;
		
		public function BindableCore()
		{
			super();
			dispatcher   = new SmartEventDispatcher();
			item	     = new Dictionary(true);
			propertyList = new Array();
		}
		
		/* proxy methods */
		
		override flash_proxy function callProperty(methodName:*, ...args):*
		{
			return this[methodName].apply(this, args);
		}
		
		override flash_proxy function deleteProperty(name:*):Boolean
		{
			if(name in item)
			{
				var oldValue:* = item[name];
				delete item[name];
				dispatchEvent(new DataEvent(DataEvent.PROPERTY_CHANGED, {property:name.localName}));
				return true;
			}
			return false;
		}
		
		override flash_proxy function getDescendants(name:*):*
		{
			Debugger.log("Descendants operator (..) not supported on type BindableObject.", "error");
		}
		
		override flash_proxy function getProperty(name:*):*
		{
			return item[name];
		}
		
		override flash_proxy function hasProperty(name:*):Boolean
		{
			return name in item;
		}
		
		override flash_proxy function nextName(index:int):String
		{
			return propertyList[index-1];
		}
		
		override flash_proxy function nextNameIndex(index:int):int
		{
			if(index == 0) setupPropertyList();
			return index < propertyList.length ? index + 1: 0;
		}
		
		override flash_proxy function nextValue(index:int):*
		{
			if(index == 0) setupPropertyList();
			return item[propertyList[index - 1]];
		}
		
		override flash_proxy function setProperty(name:*, value:*):void
		{
			var oldValue:* = item[name];
			if(value != oldValue)
			{
				item[name] = value;
				dispatchEvent(new DataEvent(DataEvent.PROPERTY_CHANGED, {property:name.localName, newValue:value, oldValue:oldValue}))
				handlePropertyChange({property:name.localName, newValue:value, oldValue:oldValue});
			}
		}
		
		/* private methods*/
		
		private function setupPropertyList():void
		{
			propertyList = [];
			for(var x:* in item) 
				propertyList.push(x);
		}
		
		/* public methods */		
		
		public function log(outputItem:*, type:String = null):void
		{
			Debugger.log(outputItem, this, type);
		}
		
		public function clear():void
		{
			// for overrid
		}
		
		public function handlePropertyChange(object:Object):void
		{
			// for override
		}
		
		public function removeAllListeners(type:String=null):void
		{
			dispatcher.removeAllListeners(type);
		}
		
		public function addEventListener(type:String, listener:Function, useCapture:Boolean=false, priority:int=0, useWeakReference:Boolean=false):void
		{
			dispatcher.addEventListener(type, listener, useCapture, priority, useWeakReference);
		}
		
		public function removeEventListener(type:String, listener:Function, useCapture:Boolean=false):void
		{
			dispatcher.removeEventListener(type, listener, useCapture);
		}
		
		public function dispatchEvent(event:Event):Boolean
		{
			return dispatcher.dispatchEvent(event);
		}
		
		public function hasEventListener(type:String):Boolean
		{
			return dispatcher.hasEventListener(type);
		}
		
		public function willTrigger(type:String):Boolean
		{
			return dispatcher.willTrigger(type);
		}
	}
}